package com.cn.steam.iam.service;

import com.cn.steam.foundation.common.utils.Op;
import com.cn.steam.iam.model.ClientDetailsWrap;
import com.cn.steam.iam.model.IamUserDetails;
import com.cn.steam.iam.model.po.IamPartner;
import com.cn.steam.iam.model.po.IamPermission;
import com.cn.steam.iam.model.po.IamUserGranter;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Sets;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.ClientRegistrationException;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.cn.steam.iam.model.PermissionGrantedAuthority.convertFromIamPermission;

/**
 * @author: zjm
 * @create: 2021-11-14 19:19
 **/
@Service
public class Oauth2ClientDetailService implements ClientDetailsService {
    @Autowired
    private IamUserProfileService iamUserProfileService;

    @Autowired
    private IamPartnerProfileService partnerProfileService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    private LoadingCache<String, ClientDetailsWrap> loadingUserByLoginNameCache = CacheBuilder.newBuilder()
            .expireAfterAccess(30, TimeUnit.MINUTES)
            .maximumSize(100)
            .build(new CacheLoader<String, ClientDetailsWrap>() {
                @Override
                public ClientDetailsWrap load(String s) throws Exception {
                    IamUserDetails iamUser = iamUserProfileService.selectByLoginName(s);
                    if(iamUser == null){
                        return null;
                    }
                    IamPartner partner = partnerProfileService.getById(iamUser.getPartnerId());
                    List<IamUserGranter> granters = iamUserProfileService.listUserGranter(iamUser.getUserId());
                    List<IamPermission> permissions = iamUserProfileService.listUserPermissions(iamUser.getUserId());

                    Set<String>  grantTypeSet = Op.isEmpty(granters) ? Sets.newHashSet()
                            :granters.stream().map(IamUserGranter::getGrantType).collect(Collectors.toSet());
                    iamUser.setGrantTypeSet(grantTypeSet);
                    iamUser.setPermissionsSet(convertFromIamPermission(permissions));
                    iamUser.setPassword(passwordEncoder.encode(iamUser.getPassword()));
                    return new ClientDetailsWrap(iamUser,partner);
                }
            });

    @Override
    public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
        clientId = Op.safeTrimString(clientId);
        if (Op.isEmpty(clientId)){
            throw new InvalidTokenException(OAuth2Exception.INVALID_TOKEN);
        }
        return getClientDetailsWithCache(clientId);
    }

    private ClientDetailsWrap getClientDetailsWithCache(String clientId){
        try {
            return loadingUserByLoginNameCache.get(clientId);
        }catch (ExecutionException e){
            throw new InvalidTokenException(OAuth2Exception.INVALID_TOKEN);
        } catch (CacheLoader.InvalidCacheLoadException e) {
            throw new InvalidTokenException(OAuth2Exception.INVALID_TOKEN);
        }
    }
}
